﻿
namespace Patterns4Net.Core.CecilExtensions.CILPatternsMatching
{
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using Mono.Cecil.Cil;

    public class CILPatternsMatcher
    {
        private readonly InstructionMatcher[] matchers;

        public CILPatternsMatcher(params InstructionMatcher[] matchers)
        {
            Contract.Requires(matchers != null);
            Contract.Requires(matchers.Length > 0);
            Contract.Ensures(this.matchers == matchers);
            this.matchers = matchers;
        }

        public bool Matches(Instruction startInstruction)
        {
            Instruction dummyNext;
            return this.Matches(startInstruction, out dummyNext);
        }

        internal bool Matches(Instruction startInstruction, out Instruction next)
        {
            var currentInstruction = startInstruction;
            var currentMatcherIndex = 0;
            while (true)
            {
                var currentMatcher = this.matchers[currentMatcherIndex];
                Instruction nextInstruction;
                bool matches = currentMatcher.Matches(currentInstruction, out nextInstruction);

                // are we at the end?
                if (nextInstruction == null)
                {
                    next = null;
                    return currentMatcherIndex == this.matchers.Length - 1 && currentMatcher.Found;
                }

                if (matches == false)
                {
                    next = nextInstruction;
                    return false;
                }

                if (currentMatcher.Found)
                {
                    currentMatcher.Reset(); // Just to make sure - if we run the Matches method again for exmaple
                    if (currentMatcherIndex == this.matchers.Length - 1)
                    {
                        // we found a whole match
                        next = nextInstruction;
                        return true;
                    }

                    currentMatcherIndex++;
                }

                currentInstruction = nextInstruction;
            }
        }
    }
}
